﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="CMDenseMatrixExtensions.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleComplexExtensions
{
    #region Usings

    using System;
    using System.Globalization;

    using ExulLibrary.Exceptions;

    #endregion

    /// <summary>
    ///     Provides extension methods for <see cref="CMDenseMatrix{T}"/> with <see cref="DoubleComplex"/> type parameter.
    /// </summary>
    public static class CMDenseMatrixExtensions
    {
        #region Methods

        #region public

        /// <summary>
        ///     Adds the specified factor to all elements of the matrix.
        /// </summary>
        /// <param name="matrix">
        ///     The matrix whose elements should be added.
        /// </param>
        /// <param name="factor">
        ///     The factor to add.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="matrix"/> is <see langword="null"/>.
        /// </exception>
        public static void Add(this CMDenseMatrix<DoubleComplex> matrix, DoubleComplex factor)
        {
#if SAFE

            if (matrix == null)
            {
                throw new ArgumentNullException("matrix");
            }

#endif

            var values = matrix.Values;
            var rowsCount = matrix.RowsCount;

            var columnsCount = matrix.ColumnsCount;
            for (var columnIndex = 0; columnIndex < columnsCount; columnIndex++)
            {
                var column = values[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCount; rowIndex++)
                {
                    column[rowIndex] += factor;
                }
            }
        }

        /// <summary>
        ///     Adds the second matrix to the first matrix.
        /// </summary>
        /// <param name="augend">
        ///     The augend matrix.
        /// </param>
        /// <param name="addend">
        ///     The addend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.RowsCount, <paramref name="addend"/>.RowsCount).
        ///     The number of columns of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.ColumnsCount, <paramref name="addend"/>.ColumnsCount).
        /// </remarks>
        public static void Add(this CMDenseMatrix<DoubleComplex> augend, CMDenseMatrix<DoubleComplex> addend)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var rowsCountToAdd = Math.Min(augend.RowsCount, addend.RowsCount);

            var columnsCountToAdd = Math.Min(augend.ColumnsCount, addend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToAdd; columnIndex++)
            {
                var augendColumn = augendValues[columnIndex];
                var addendColumn = addendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToAdd; rowIndex++)
                {
                    augendColumn[rowIndex] += addendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Adds the second matrix to the first matrix multiplied by the factor.
        /// </summary>
        /// <param name="augend">
        ///     The augend matrix.
        /// </param>
        /// <param name="augendFactor">
        ///     The factor of the augend matrix.
        /// </param>
        /// <param name="addend">
        ///     The addend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.RowsCount, <paramref name="addend"/>.RowsCount).
        ///     The number of columns of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.ColumnsCount, <paramref name="addend"/>.ColumnsCount).
        /// </remarks>
        public static void Add(
            this CMDenseMatrix<DoubleComplex> augend, DoubleComplex augendFactor, CMDenseMatrix<DoubleComplex> addend)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var rowsCountToAdd = Math.Min(augend.RowsCount, addend.RowsCount);

            var columnsCountToAdd = Math.Min(augend.ColumnsCount, addend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToAdd; columnIndex++)
            {
                var augendColumn = augendValues[columnIndex];
                var addendColumn = addendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToAdd; rowIndex++)
                {
                    augendColumn[rowIndex] = (augendFactor * augendColumn[rowIndex]) + addendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Adds the second matrix multiplied by the factor to the first matrix.
        /// </summary>
        /// <param name="augend">
        ///     The augend matrix.
        /// </param>
        /// <param name="addend">
        ///     The addend matrix.
        /// </param>
        /// <param name="addendFactor">
        ///     The factor of the addend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.RowsCount, <paramref name="addend"/>.RowsCount).
        ///     The number of columns of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.ColumnsCount, <paramref name="addend"/>.ColumnsCount).
        /// </remarks>
        public static void Add(
            this CMDenseMatrix<DoubleComplex> augend, CMDenseMatrix<DoubleComplex> addend, DoubleComplex addendFactor)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var rowsCountToAdd = Math.Min(augend.RowsCount, addend.RowsCount);

            var columnsCountToAdd = Math.Min(augend.ColumnsCount, addend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToAdd; columnIndex++)
            {
                var augendColumn = augendValues[columnIndex];
                var addendColumn = addendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToAdd; rowIndex++)
                {
                    augendColumn[rowIndex] += addendFactor * addendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Adds the second matrix multiplied by the second factor to the first matrix multiplied by the first factor.
        /// </summary>
        /// <param name="augend">
        ///     The augend matrix.
        /// </param>
        /// <param name="augendFactor">
        ///     The factor of the augend matrix.
        /// </param>
        /// <param name="addend">
        ///     The addend matrix.
        /// </param>
        /// <param name="addendFactor">
        ///     The factor of the addend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.RowsCount, <paramref name="addend"/>.RowsCount).
        ///     The number of columns of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.ColumnsCount, <paramref name="addend"/>.ColumnsCount).
        /// </remarks>
        public static void Add(
            this CMDenseMatrix<DoubleComplex> augend,
            DoubleComplex augendFactor,
            CMDenseMatrix<DoubleComplex> addend,
            DoubleComplex addendFactor)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var rowsCountToAdd = Math.Min(augend.RowsCount, addend.RowsCount);

            var columnsCountToAdd = Math.Min(augend.ColumnsCount, addend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToAdd; columnIndex++)
            {
                var augendColumn = augendValues[columnIndex];
                var addendColumn = addendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToAdd; rowIndex++)
                {
                    augendColumn[rowIndex] = (augendFactor * augendColumn[rowIndex]) + (addendFactor * addendColumn[rowIndex]);
                }
            }
        }

        /// <summary>
        ///     Adds the second matrix to the specified elements of the first matrix.
        /// </summary>
        /// <param name="augend">
        ///     The augend matrix.
        /// </param>
        /// <param name="augendRowsIndexes">
        ///     The collection of zero-based indexes of rows of augend elements.
        /// </param>
        /// <param name="augendColumnsIndexes">
        ///     The collection of zero-based indexes of columns of augend elements.
        /// </param>
        /// <param name="addend">
        ///     The addend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="augendRowsIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="augendColumnsIndexes"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="augendRowsIndexes"/> is negative or greater than or equal to
        ///     rows count of <paramref name="augend"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         An element of <paramref name="augendColumnsIndexes"/> is negative or greater than or equal to
        ///         columns count of <paramref name="augend"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="addend"/> to add is determined as
        ///     min(<paramref name="augendRowsIndexes"/>.Length, <paramref name="addend"/>.RowsCount).
        ///     The number of columns of <paramref name="addend"/> to add is determined as
        ///     min(<paramref name="augendColumnsIndexes"/>.Length, <paramref name="addend"/>.ColumnsCount).
        /// </remarks>
        public static void Add(
            this CMDenseMatrix<DoubleComplex> augend,
            int[] augendRowsIndexes,
            int[] augendColumnsIndexes,
            CMDenseMatrix<DoubleComplex> addend)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (augendRowsIndexes == null)
            {
                throw new ArgumentNullException("augendRowsIndexes");
            }

            if (augendColumnsIndexes == null)
            {
                throw new ArgumentNullException("augendColumnsIndexes");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendRowsCount = augend.RowsCount;
            var augendColumnsCount = augend.ColumnsCount;
            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var rowsCountToAdd = Math.Min(augendRowsIndexes.Length, addend.RowsCount);

            var columnsCountToAdd = Math.Min(augendColumnsIndexes.Length, addend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToAdd; columnIndex++)
            {
                var columnElementIndex = augendColumnsIndexes[columnIndex];

#if SAFE

                if ((columnElementIndex < 0) || (augendColumnsCount <= columnElementIndex))
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "augendColumnsIndexes[{0}]", columnIndex);
                    throw new ElementIndexIsOutOfRangeException(argumentName, columnElementIndex, 0, augendColumnsCount - 1);
                }

#endif

                var augendColumn = augendValues[columnElementIndex];
                var addendColumn = addendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToAdd; rowIndex++)
                {
                    var rowElementIndex = augendRowsIndexes[rowIndex];

#if SAFE

                    if ((rowElementIndex < 0) || (augendRowsCount <= rowElementIndex))
                    {
                        var argumentName = string.Format(CultureInfo.CurrentCulture, "augendRowsIndexes[{0}]", rowIndex);
                        throw new ElementIndexIsOutOfRangeException(argumentName, rowElementIndex, 0, augendRowsCount - 1);
                    }

#endif

                    augendColumn[rowElementIndex] += addendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Adds the second matrix multiplied by the factor to the specified elements of the first matrix.
        /// </summary>
        /// <param name="augend">
        ///     The augend matrix.
        /// </param>
        /// <param name="augendRowsIndexes">
        ///     The collection of zero-based indexes of rows of augend elements.
        /// </param>
        /// <param name="augendColumnsIndexes">
        ///     The collection of zero-based indexes of columns of augend elements.
        /// </param>
        /// <param name="addend">
        ///     The addend matrix.
        /// </param>
        /// <param name="addendFactor">
        ///     The factor of the addend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="augendRowsIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="augendColumnsIndexes"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="augendRowsIndexes"/> is negative or greater than or equal to
        ///     rows count of <paramref name="augend"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         An element of <paramref name="augendColumnsIndexes"/> is negative or greater than or equal to
        ///         columns count of <paramref name="augend"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="addend"/> to add is determined as
        ///     min(<paramref name="augendRowsIndexes"/>.Length, <paramref name="addend"/>.RowsCount).
        ///     The number of columns of <paramref name="addend"/> to add is determined as
        ///     min(<paramref name="augendColumnsIndexes"/>.Length, <paramref name="addend"/>.ColumnsCount).
        /// </remarks>
        public static void Add(
            this CMDenseMatrix<DoubleComplex> augend,
            int[] augendRowsIndexes,
            int[] augendColumnsIndexes,
            CMDenseMatrix<DoubleComplex> addend,
            DoubleComplex addendFactor)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (augendRowsIndexes == null)
            {
                throw new ArgumentNullException("augendRowsIndexes");
            }

            if (augendColumnsIndexes == null)
            {
                throw new ArgumentNullException("augendColumnsIndexes");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendRowsCount = augend.RowsCount;
            var augendColumnsCount = augend.ColumnsCount;
            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var rowsCountToAdd = Math.Min(augendRowsIndexes.Length, addend.RowsCount);

            var columnsCountToAdd = Math.Min(augendColumnsIndexes.Length, addend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToAdd; columnIndex++)
            {
                var columnElementIndex = augendColumnsIndexes[columnIndex];

#if SAFE

                if ((columnElementIndex < 0) || (augendColumnsCount <= columnElementIndex))
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "augendColumnsIndexes[{0}]", columnIndex);
                    throw new ElementIndexIsOutOfRangeException(argumentName, columnElementIndex, 0, augendColumnsCount - 1);
                }

#endif

                var augendColumn = augendValues[columnElementIndex];
                var addendColumn = addendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToAdd; rowIndex++)
                {
                    var rowElementIndex = augendRowsIndexes[rowIndex];

#if SAFE

                    if ((rowElementIndex < 0) || (augendRowsCount <= rowElementIndex))
                    {
                        var argumentName = string.Format(CultureInfo.CurrentCulture, "augendRowsIndexes[{0}]", rowIndex);
                        throw new ElementIndexIsOutOfRangeException(argumentName, rowElementIndex, 0, augendRowsCount - 1);
                    }

#endif

                    augendColumn[rowElementIndex] += addendFactor * addendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Multiplies all elements of the matrix by the specified factor.
        /// </summary>
        /// <param name="matrix">
        ///     The matrix whose elements should be multiplied.
        /// </param>
        /// <param name="factor">
        ///     The factor to multiply.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="matrix"/> is <see langword="null"/>.
        /// </exception>
        public static void Multiply(this CMDenseMatrix<DoubleComplex> matrix, DoubleComplex factor)
        {
#if SAFE

            if (matrix == null)
            {
                throw new ArgumentNullException("matrix");
            }

#endif

            var values = matrix.Values;
            var rowsCount = matrix.RowsCount;

            var columnsCount = matrix.ColumnsCount;
            for (var columnIndex = 0; columnIndex < columnsCount; columnIndex++)
            {
                var column = values[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCount; rowIndex++)
                {
                    column[rowIndex] *= factor;
                }
            }
        }

        /// <summary>
        ///     Multiplies the matrix by the dense vector and stores the result in the another vector.
        /// </summary>
        /// <param name="multiplicand">
        ///     The multiplicand matrix.
        /// </param>
        /// <param name="multiplier">
        ///     The multiplier vector.
        /// </param>
        /// <param name="result">
        ///     The vector to store the result.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="multiplicand"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="multiplier"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="result"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="multiplicand"/> to multiply is determined as
        ///     min(<paramref name="multiplicand"/>.RowsCount, <paramref name="result"/>.Length).
        ///     The number of columns of <paramref name="multiplicand"/> to multiply is determined as
        ///     min(<paramref name="multiplicand"/>.ColumnsCount, <paramref name="multiplier"/>.Length).
        /// </remarks>
        public static void Multiply(
            this CMDenseMatrix<DoubleComplex> multiplicand,
            DenseVector<DoubleComplex> multiplier,
            DenseVector<DoubleComplex> result)
        {
#if SAFE

            if (multiplicand == null)
            {
                throw new ArgumentNullException("multiplicand");
            }

            if (multiplier == null)
            {
                throw new ArgumentNullException("multiplier");
            }

            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

#endif

            var multiplicandValues = multiplicand.Values;
            var multiplierValues = multiplier.Values;
            var resultValues = result.Values;

            result.Clear();

            var rowsCountToMultiply = Math.Min(multiplicand.RowsCount, result.Length);

            var columnsCountToMultiply = Math.Min(multiplicand.ColumnsCount, multiplier.Length);
            for (var columnIndex = 0; columnIndex < columnsCountToMultiply; columnIndex++)
            {
                var multiplicandColumn = multiplicandValues[columnIndex];

                var multiplierElement = multiplierValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToMultiply; rowIndex++)
                {
                    resultValues[rowIndex] += multiplicandColumn[rowIndex] * multiplierElement;
                }
            }
        }

        /// <summary>
        ///     Subtracts the second matrix from the first matrix.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend matrix.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.RowsCount, <paramref name="subtrahend"/>.RowsCount).
        ///     The number of columns of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.ColumnsCount, <paramref name="subtrahend"/>.ColumnsCount).
        /// </remarks>
        public static void Subtract(this CMDenseMatrix<DoubleComplex> minuend, CMDenseMatrix<DoubleComplex> subtrahend)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var rowsCountToSubtract = Math.Min(minuend.RowsCount, subtrahend.RowsCount);

            var columnsCountToSubtract = Math.Min(minuend.ColumnsCount, subtrahend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToSubtract; columnIndex++)
            {
                var minuendColumn = minuendValues[columnIndex];
                var subtrahendColumn = subtrahendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToSubtract; rowIndex++)
                {
                    minuendColumn[rowIndex] -= subtrahendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Subtracts the second matrix from the first matrix multiplied by the factor.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend matrix.
        /// </param>
        /// <param name="minuendFactor">
        ///     The factor of the minuend matrix.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.RowsCount, <paramref name="subtrahend"/>.RowsCount).
        ///     The number of columns of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.ColumnsCount, <paramref name="subtrahend"/>.ColumnsCount).
        /// </remarks>
        public static void Subtract(
            this CMDenseMatrix<DoubleComplex> minuend, DoubleComplex minuendFactor, CMDenseMatrix<DoubleComplex> subtrahend)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var rowsCountToSubtract = Math.Min(minuend.RowsCount, subtrahend.RowsCount);

            var columnsCountToSubtract = Math.Min(minuend.ColumnsCount, subtrahend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToSubtract; columnIndex++)
            {
                var minuendColumn = minuendValues[columnIndex];
                var subtrahendColumn = subtrahendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToSubtract; rowIndex++)
                {
                    minuendColumn[rowIndex] = (minuendFactor * minuendColumn[rowIndex]) - subtrahendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Subtracts the second matrix multiplied by the factor from the first matrix.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend matrix.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend matrix.
        /// </param>
        /// <param name="subtrahendFactor">
        ///     The factor of the subtrahend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.RowsCount, <paramref name="subtrahend"/>.RowsCount).
        ///     The number of columns of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.ColumnsCount, <paramref name="subtrahend"/>.ColumnsCount).
        /// </remarks>
        public static void Subtract(
            this CMDenseMatrix<DoubleComplex> minuend, CMDenseMatrix<DoubleComplex> subtrahend, DoubleComplex subtrahendFactor)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var rowsCountToSubtract = Math.Min(minuend.RowsCount, subtrahend.RowsCount);

            var columnsCountToSubtract = Math.Min(minuend.ColumnsCount, subtrahend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToSubtract; columnIndex++)
            {
                var minuendColumn = minuendValues[columnIndex];
                var subtrahendColumn = subtrahendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToSubtract; rowIndex++)
                {
                    minuendColumn[rowIndex] -= subtrahendFactor * subtrahendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Subtracts the second matrix multiplied by the second factor from the first matrix multiplied by the first factor.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend matrix.
        /// </param>
        /// <param name="minuendFactor">
        ///     The factor of the minuend matrix.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend matrix.
        /// </param>
        /// <param name="subtrahendFactor">
        ///     The factor of the subtrahend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.RowsCount, <paramref name="subtrahend"/>.RowsCount).
        ///     The number of columns of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.ColumnsCount, <paramref name="subtrahend"/>.ColumnsCount).
        /// </remarks>
        public static void Subtract(
            this CMDenseMatrix<DoubleComplex> minuend,
            DoubleComplex minuendFactor,
            CMDenseMatrix<DoubleComplex> subtrahend,
            DoubleComplex subtrahendFactor)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var rowsCountToSubtract = Math.Min(minuend.RowsCount, subtrahend.RowsCount);

            var columnsCountToSubtract = Math.Min(minuend.ColumnsCount, subtrahend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToSubtract; columnIndex++)
            {
                var minuendColumn = minuendValues[columnIndex];
                var subtrahendColumn = subtrahendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToSubtract; rowIndex++)
                {
                    minuendColumn[rowIndex] =
                        (minuendFactor * minuendColumn[rowIndex]) - (subtrahendFactor * subtrahendColumn[rowIndex]);
                }
            }
        }

        /// <summary>
        ///     Subtracts the second matrix from the specified elements of the first matrix.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend matrix.
        /// </param>
        /// <param name="minuendRowsIndexes">
        ///     The collection of zero-based indexes of rows of minuend elements.
        /// </param>
        /// <param name="minuendColumnsIndexes">
        ///     The collection of zero-based indexes of columns of minuend elements.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="minuendRowsIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="minuendColumnsIndexes"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="minuendRowsIndexes"/> is negative or greater than or equal to
        ///     rows count of <paramref name="minuend"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         An element of <paramref name="minuendColumnsIndexes"/> is negative or greater than or equal to
        ///         columns count of <paramref name="minuend"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="subtrahend"/> to subtract is determined as
        ///     min(<paramref name="minuendRowsIndexes"/>.Length, <paramref name="subtrahend"/>.RowsCount).
        ///     The number of columns of <paramref name="subtrahend"/> to subtract is determined as
        ///     min(<paramref name="minuendColumnsIndexes"/>.Length, <paramref name="subtrahend"/>.ColumnsCount).
        /// </remarks>
        public static void Subtract(
            this CMDenseMatrix<DoubleComplex> minuend,
            int[] minuendRowsIndexes,
            int[] minuendColumnsIndexes,
            CMDenseMatrix<DoubleComplex> subtrahend)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (minuendRowsIndexes == null)
            {
                throw new ArgumentNullException("minuendRowsIndexes");
            }

            if (minuendColumnsIndexes == null)
            {
                throw new ArgumentNullException("minuendColumnsIndexes");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendRowsCount = minuend.RowsCount;
            var minuendColumnsCount = minuend.ColumnsCount;
            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var rowsCountToSubtract = Math.Min(minuendRowsIndexes.Length, subtrahend.RowsCount);

            var columnsCountToSubtract = Math.Min(minuendColumnsIndexes.Length, subtrahend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToSubtract; columnIndex++)
            {
                var columnElementIndex = minuendColumnsIndexes[columnIndex];

#if SAFE

                if ((columnElementIndex < 0) || (minuendColumnsCount <= columnElementIndex))
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "minuendColumnsIndexes[{0}]", columnIndex);
                    throw new ElementIndexIsOutOfRangeException(argumentName, columnElementIndex, 0, minuendColumnsCount - 1);
                }

#endif

                var minuendColumn = minuendValues[columnElementIndex];
                var subtrahendColumn = subtrahendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToSubtract; rowIndex++)
                {
                    var rowElementIndex = minuendRowsIndexes[rowIndex];

#if SAFE

                    if ((rowElementIndex < 0) || (minuendRowsCount <= rowElementIndex))
                    {
                        var argumentName = string.Format(
                            CultureInfo.CurrentCulture, "minuendRowsIndexes[{0}]", rowIndex);
                        throw new ElementIndexIsOutOfRangeException(argumentName, rowElementIndex, 0, minuendRowsCount - 1);
                    }

#endif

                    minuendColumn[rowElementIndex] -= subtrahendColumn[rowIndex];
                }
            }
        }

        /// <summary>
        ///     Subtracts the second matrix multiplied by the factor from the specified elements of the first matrix.
        /// </summary>
        /// <param name="minuend">
        ///     The  minuend matrix.
        /// </param>
        /// <param name="minuendRowsIndexes">
        ///     The collection of zero-based indexes of rows of minuend elements.
        /// </param>
        /// <param name="minuendColumnsIndexes">
        ///     The collection of zero-based indexes of columns of minuend elements.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend matrix.
        /// </param>
        /// <param name="subtrahendFactor">
        ///     The factor of the subtrahend matrix.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="minuendRowsIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="minuendColumnsIndexes"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="minuendRowsIndexes"/> is negative or greater than or equal to
        ///     rows count of <paramref name="minuend"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         An element of <paramref name="minuendColumnsIndexes"/> is negative or greater than or equal to
        ///         columns count of <paramref name="minuend"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of rows of <paramref name="subtrahend"/> to subtract is determined as
        ///     min(<paramref name="minuendRowsIndexes"/>.Length, <paramref name="subtrahend"/>.RowsCount).
        ///     The number of columns of <paramref name="subtrahend"/> to subtract is determined as
        ///     min(<paramref name="minuendColumnsIndexes"/>.Length, <paramref name="subtrahend"/>.ColumnsCount).
        /// </remarks>
        public static void Subtract(
            this CMDenseMatrix<DoubleComplex> minuend,
            int[] minuendRowsIndexes,
            int[] minuendColumnsIndexes,
            CMDenseMatrix<DoubleComplex> subtrahend,
            DoubleComplex subtrahendFactor)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (minuendRowsIndexes == null)
            {
                throw new ArgumentNullException("minuendRowsIndexes");
            }

            if (minuendColumnsIndexes == null)
            {
                throw new ArgumentNullException("minuendColumnsIndexes");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendRowsCount = minuend.RowsCount;
            var minuendColumnsCount = minuend.ColumnsCount;
            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var rowsCountToSubtract = Math.Min(minuendRowsIndexes.Length, subtrahend.RowsCount);

            var columnsCountToSubtract = Math.Min(minuendColumnsIndexes.Length, subtrahend.ColumnsCount);
            for (var columnIndex = 0; columnIndex < columnsCountToSubtract; columnIndex++)
            {
                var columnElementIndex = minuendColumnsIndexes[columnIndex];

#if SAFE

                if ((columnElementIndex < 0) || (minuendColumnsCount <= columnElementIndex))
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "minuendColumnsIndexes[{0}]", columnIndex);
                    throw new ElementIndexIsOutOfRangeException(argumentName, columnElementIndex, 0, minuendColumnsCount - 1);
                }

#endif

                var minuendColumn = minuendValues[columnElementIndex];
                var subtrahendColumn = subtrahendValues[columnIndex];

                for (var rowIndex = 0; rowIndex < rowsCountToSubtract; rowIndex++)
                {
                    var rowElementIndex = minuendRowsIndexes[rowIndex];

#if SAFE

                    if ((rowElementIndex < 0) || (minuendRowsCount <= rowElementIndex))
                    {
                        var argumentName = string.Format(
                            CultureInfo.CurrentCulture, "minuendRowsIndexes[{0}]", rowIndex);
                        throw new ElementIndexIsOutOfRangeException(argumentName, rowElementIndex, 0, minuendRowsCount - 1);
                    }

#endif

                    minuendColumn[rowElementIndex] -= subtrahendFactor * subtrahendColumn[rowIndex];
                }
            }
        }

        #endregion

        #endregion
    }
}